/// 多行输入框，使用command+Enter键触发提交函数
part of 'widgets.dart';

class LxInput extends StatefulWidget {
  /// 由于有时候会需要根据输入框内的文字长度来决定其他UI的外观，因此使用Rx值，每当有什么变化的时候自动将变化传递到Rx值内
  /// 一旦传入，如果修改rx值，那么也会同步修改controller的text值（双向绑定）
  final RxString rxText;
  final String? hint;

  /// Windows是alt+Enter，MacOS中是Meta+Enter
  final Function()? onSubmit;

  /// 当用户暂停输入时，比如停止输入2秒后，会触发此函数
  /// 可以加入一些自动保存的逻辑
  final Function()? onPause;

  /// 初始化的时候可以自行设定node的行为，比如在外界启动一些动画之类的
  final Function(FocusNode)? onInit;

  /// 暂停的间隔，默认为2秒，可以调整
  final Duration pauseInterval;

  // 是否为多行输入
  final bool multiline;
  final bool autofocus;
  // 比如有时候不想让用户输入的时候，设置这个值
  final bool readOnly;

  final RxInputTheme? theme;

  const LxInput(
    this.rxText, {
    this.hint,
    this.onSubmit,
    this.onPause,
    this.onInit,
    this.pauseInterval = const Duration(seconds: 2),
    this.multiline = false,
    this.autofocus = false,
    this.readOnly = false,
    this.theme,
    super.key,
  });

  @override
  State<LxInput> createState() => _LxInputState();
}

class _LxInputState extends State<LxInput> {
  final ctrl = TextEditingController();
  // focusNode必须在state中，否则每次build就会丢失光标
  final node = FocusNode();

  late final Worker stopWorker;
  late final Worker changeWorker;

  @override
  void initState() {
    super.initState();

    /// 1. 在初始化时，对rxText和controller进行双向绑定
    // 每当输入框变化时，会同步更新rx值
    ctrl.addListener(() {
      // 让rx对齐控制器
      if (widget.rxText.value != ctrl.text) widget.rxText.value = ctrl.text;
    });
    // 每当rx值变化时，同步更新控制器
    changeWorker = ever(widget.rxText, (String value) {
      if (ctrl.text != value) ctrl.text = value;
    });
    // 首次加载时，以rx值为准
    if (widget.rxText.value != ctrl.text) ctrl.text = widget.rxText.value;

    /// 2. 设置3秒的自动保存逻辑
    stopWorker = debounce(
      widget.rxText,
      (_) => widget.onPause?.call(),
      time: widget.pauseInterval,
    );

    /// 设置初始化
    widget.onInit?.call(node);
  }

  @override
  void dispose() {
    super.dispose();
    ctrl.dispose();
    node.dispose();
    changeWorker.dispose();
    stopWorker.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final theme = widget.theme ?? RxInputTheme.primary();

    // 任何处理函数都在build中传入，这样可以及时响应函数的变化
    node.onKeyEvent = bindMetaEnter(widget.onSubmit);

    return TextField(
      focusNode: node,
      autofocus: widget.autofocus,
      autocorrect: false,
      readOnly: widget.readOnly,
      controller: ctrl,
      maxLines: widget.multiline ? null : 1,
      keyboardType: widget.multiline ? TextInputType.multiline : null,
      style: theme.style,
      textAlign: theme.textAlign,
      decoration: decorationNone.copyWith(
        hintText: widget.hint,
        hintStyle: theme.style.copyWith(
          color: theme.style.color?.withOpacity(theme.hintOpacity ?? 0.3),
        ),
      ),
      cursorColor: theme.cursorColor,
      cursorWidth: theme.cursorWidth ?? 2,
      cursorHeight: theme.cursorHeight,
    );
  }
}

/// 一个高阶函数，传入submit处理函数，返回一个KeyEvent处理函数
KeyEventResult Function(FocusNode, KeyEvent)? bindMetaEnter(Function? submit) =>
    (_, event) {
      bool isCorrectKeyCombination;

      // 在这里，HardwareKeyboard判断的是“预先按下”，也就是在辅助键按下的前提下，按了enter键
      if (Platform.isWindows) {
        // Windows: Alt + Enter
        isCorrectKeyCombination = event is KeyDownEvent &&
            HardwareKeyboard.instance.isAltPressed &&
            event.logicalKey == LogicalKeyboardKey.enter;
      } else {
        // macOS, Linux, Web, etc.: Meta + Enter
        isCorrectKeyCombination = event is KeyDownEvent &&
            HardwareKeyboard.instance.isMetaPressed &&
            event.logicalKey == LogicalKeyboardKey.enter;
      }

      // 如果是Meta+Enter键，并且传入了函数的话，则执行函数，并中止传递链
      if (isCorrectKeyCombination && submit != null) {
        submit.call();
        return KeyEventResult.handled;
      }
      // 否则继续传递
      return KeyEventResult.ignored;
    };

class RxInputTheme {
  late final TextStyle style;
  final TextAlign textAlign;
  final Color? cursorColor;
  final double? cursorWidth;
  final double? cursorHeight;
  final double? hintOpacity;
  RxInputTheme({
    TextStyle? style,
    this.cursorColor,
    this.cursorWidth,
    this.cursorHeight,
    this.hintOpacity,
    this.textAlign = TextAlign.start,
  }) {
    this.style = style ??
        TextStyle(
          fontFamily: "思源黑体",
          fontWeight: FontWeight.w400,
          letterSpacing: 1.9,
          height: 2,
          color: COLOR.greyLight.darken(0.2),
        );
  }

  factory RxInputTheme.primary() => RxInputTheme(
        cursorColor: COLOR.greyLight,
        cursorHeight: 22,
        cursorWidth: 3,
      );
}

/// 去除一切默认装饰
const decorationNone = const InputDecoration(
  counterText: "",
  isDense: true,
  border: OutlineInputBorder(
    borderSide: BorderSide.none,
  ),
  contentPadding: EdgeInsets.zero,
);
